package com.lambkit.core.http;

import cn.hutool.core.lang.Filter;
import cn.hutool.core.util.ClassUtil;
import com.lambkit.core.AppContext;
import com.lambkit.core.Lifecycle;
import com.lambkit.core.LifecycleException;
import com.lambkit.core.LifecycleState;
import com.lambkit.util.Printer;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * @author yangyong(孤竹行)
 */
public class HttpHandlers implements Lifecycle {

    private List<Handler> handlers = new ArrayList<>();
    private ConcurrentMap<String, HttpAction> actions = new ConcurrentHashMap<>(5);
    private LifecycleState currentState = LifecycleState.NEW;

    private ParamsBuilder paramsBuilder = new ParamsBuilder();

    private AppContext appContext;

    public void runOn(AppContext appContext) {
        this.appContext = appContext;
    }

    public void addHandler(Handler handler) {
        handlers.add(handler);
    }

    public List<Method> getOwnMethods(Class<?> clazz) {
        List<Method> ownMethods = new ArrayList<>();
        //获取所有的方法：包含父类
        //Method method[] = clazz.getMethods();
        //使用getDeclaredMethods获取其类自身声明的所有方法
        Method[] allMethods = clazz.getDeclaredMethods();
        for (Method method : allMethods) {
            if(Modifier.isPublic(method.getModifiers())) {
                ownMethods.add(method);
            }
        }
        return ownMethods;
    }

    public void addController(String route, Class controllerClass) {
        List<Method> methods = getOwnMethods(controllerClass);
        String routePrefix = route.endsWith("/") ? route : route + "/";
        for (Method method : methods) {
            String uri = routePrefix + method.getName();
            Printer.print(this, "web", "route: " + uri + " -> " + controllerClass.getName() + "." + method.getName());
            HttpAction action = new HttpAction(uri, method, controllerClass);
            if(actions.get(uri) == null) {
                actions.put(uri, action);
            } else {
                throw new RuntimeException("The uri [" + uri + "] in [" + controllerClass.getName() + "] is already exists.");
            }
        }
    }

    public boolean handle(IHttpContext httpContext) {
        String targetUri = httpContext.getTarget();
        HttpAction action = actions.get(targetUri);

        if (action != null) {
            Printer.print(this, "web", "route: " + targetUri + " -> " + targetUri);
        } else {
            int i = targetUri.lastIndexOf(47);
            if (i != -1) {
                action =  actions.get(targetUri.substring(0, i));
                Printer.print(this, "web", "route: " + targetUri + " -> " + targetUri.substring(0, i));
                if (action != null) {
                    httpContext.setUrlPara(targetUri.substring(i + 1));
                }
            }
        }

        if (action == null) {
            //正则匹配
            for(String requestMapping : actions.keySet()) {
                if(matchesPattern(targetUri, requestMapping)) {
                    action = actions.get(requestMapping);
                    Printer.print(this, "web", "route: " + targetUri + " -> " + requestMapping);
                    break;
                }
            }
        }

        if(action == null) {
            return false;
        }

        boolean flag = handleBefore(httpContext, action);
        if (flag) {
            Object[] args = paramsBuilder.buildParams(httpContext, action);
            Object target = appContext.getBean(action.getController());
            if(target instanceof IController) {
                ((IController) target).setContext(httpContext);
            }
            try {
                //反射调用method
                Object result = action.getMethod().invoke(target, args);
                if(httpContext.isPOST()) {
                    httpContext.renderJson(result);
                } else if(result != null)  {
                    if(result instanceof String) {
                        httpContext.renderHtml(result.toString());
                    } else {
                        httpContext.renderJson(result);
                    }
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        }
        handleAfter(httpContext, action);
        return true;
    }

    @Override
    public LifecycleState getCurrentState() {
        return currentState;
    }

    @Override
    public void start() throws LifecycleException {
        currentState = LifecycleState.STARTING;
        if (handlers.size() > 0) {
            //sort
            Collections.sort(handlers,
                    (Comparator<Handler>) (o1, o2)
                            -> Integer.valueOf(o1.getPriority()).compareTo(Integer.valueOf(o2.getPriority()))
            );
        }
        currentState = LifecycleState.STARTED;
    }

    @Override
    public void stop() throws LifecycleException {
        handlers.clear();
        actions.clear();
        currentState = LifecycleState.STOPPED;
    }

    public boolean handleBefore(IHttpContext context, HttpAction action) {
        //handle
        for (Handler bpp : handlers) {
            boolean isContinue = bpp.handleBefore(context, action);
            if(!isContinue) {
                return false;
            }
        }
        return true;
    }

    public void handleAfter(IHttpContext context, HttpAction action) {
        //handle
        for (Handler bpp : handlers) {
            bpp.handleAfter(context, action);
        }
    }



    public boolean matchesPattern(String targetUri, String requestMapping) {
        // 将URI组件进行分割，并构造正则表达式
        String regex = targetUri
                .replaceAll("\\{", "\\(")
                .replaceAll("\\}", "\\)")
                .replaceAll("\\*", ".*");

        // 根据模式构造正则表达式对象
        java.util.regex.Pattern p = java.util.regex.Pattern.compile(regex);

        // 对URI的各部分进行正则匹配
        return p.matcher(requestMapping).matches();
    }
}

